package com.biubiux2.handler;

import com.biubiux2.async.FutureTaskScheduler;
import com.biubiux2.async.GamerThread;
import com.biubiux2.entity.User;
import com.biubiux2.message.MessageType;
import com.biubiux2.message.MessageUtil;
import com.biubiux2.session.ServerSession;
import com.biubiux2.session.SessionMap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.handler.timeout.ReadTimeoutException;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

/**
 * @author noexcs
 * @since 8/10/2022 4:02 PM
 */
@Slf4j
public class LoginHandler extends ChannelInboundHandlerAdapter {

    private boolean logged = false;

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;
        int type = byteBuf.readInt();
        if (logged) {
            // 登出消息
            if (type == MessageType.LOGOUT) {
                ServerSession session = ctx.channel().attr(ServerSession.SESSION_ATTRIBUTE_KEY).get();
                if (session != null) {
                    User user = session.userTransmission.getUser();
                    GamerThread.submit(() -> {
                        SessionMap.getInstance().sessionMap.remove(session.getSessionId());
                        SessionMap.getInstance().uidSessionMap.remove(user.getUserId());
                        log.debug("用户{}({})退出登录", user.getUsername(), user.getUserId());
                    }, user.getUserId());
                }
                byteBuf.release();
            } else {
                // 其他消息
                ServerSession session = ctx.channel().attr(ServerSession.SESSION_ATTRIBUTE_KEY).get();
                session.lastReceivedTime = System.currentTimeMillis();
                byteBuf.resetReaderIndex();
                super.channelRead(ctx, byteBuf);
            }
        } else {
            if (type == MessageType.LOGIN_REQUEST) {
                handleLogin(ctx, byteBuf);
            } else if (type == MessageType.RECONNECT) {
                byte[] bytes = new byte[byteBuf.readableBytes()];
                byteBuf.readBytes(bytes);
                byteBuf.release();
                String sessionId = new String(bytes, StandardCharsets.UTF_8);

                log.debug("尝试重连Session ID:{}", sessionId);
                ServerSession session = SessionMap.getInstance().sessionMap.get(sessionId);
                if (session != null && !session.isExpired() && !session.getChannel().isActive()) {
                    session.setChannel(ctx.channel());
                    session.bindChannel();
                    ctx.writeAndFlush(MessageUtil.createReconnectMsg(true));
                    logged = true;
                    log.debug("重连成功");
                    return;
                }
                if (session != null) {
                    SessionMap.getInstance().sessionMap.remove(session.getSessionId());
                    SessionMap.getInstance().uidSessionMap.remove(session.userTransmission.getUser().getUserId());
                }
                ctx.writeAndFlush(MessageUtil.createReconnectMsg(false));
                log.debug("重连失败");
            } else {
                // 如果未登录，且不为登录消息和重连消息，一律不处理
                byteBuf.release();
            }
        }
    }

    public void handleLogin(ChannelHandlerContext ctx, ByteBuf byteBuf) {
        int userId = byteBuf.readInt();
        int readableBytes = byteBuf.readableBytes();
        byte[] passwordByte = new byte[readableBytes];
        byteBuf.readBytes(passwordByte);
        byteBuf.release();

        String password = new String(passwordByte, StandardCharsets.UTF_8);
        GamerThread.submit(() -> {
//            UserMapper userMapper = DaoUtil.getMapper(UserMapper.class);
//            User user = userMapper.selectPasswordAndUsername(userId);
            User user = new User(userId, String.valueOf(userId), "123456", 0);
            if (user != null && user.getPasswd().equals(password)) {
                user.setUserId(userId);
                log.debug("用户{}({})上线", user.getUsername(), userId);

                // 创建并初始化session
                ServerSession session;
                ServerSession preSession = SessionMap.getInstance().uidSessionMap.get(userId);
                if (preSession != null) {
                    // 如果有之前的Session
                    if (preSession.getChannel().isActive()) {
                        // 重复登录
                        log.debug("用户{}重复登录。", userId);
                        // 向已经存在的连接，发送重复登录消息并关闭之前已经存在的连接
                        preSession.getChannel().writeAndFlush(MessageUtil.createReLoginMsg())
                                .addListener((ChannelFutureListener) future -> future.channel().close());
                    }
                    // 断线，新设备登录，重新设置Channel,(有Session但是没连接，也没使用session id重连)
                    // 重置Session Id，防止老设备使用Session Id重连
                    preSession.updateSessionId();
                    preSession.setChannel(ctx.channel());
                    session = preSession;
                } else {
                    session = new ServerSession(ctx.channel());
                    session.addToRemoveSessionService();
                    // 添加到Session集合
                    SessionMap.getInstance().addSession(session);
                }
                session.lastReceivedTime = System.currentTimeMillis();
                session.bindChannel();

                logged = true;
                // 返回session id
                ByteBuf response = MessageUtil.createLoginSuccessMsg(session.getSessionId());
                ctx.writeAndFlush(response);
            } else {
                ctx.writeAndFlush(MessageUtil.createLoginFailMsg());
            }
        }, userId);
    }


    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
        ServerSession session = ctx.channel().attr(ServerSession.SESSION_ATTRIBUTE_KEY).get();
        if (session != null) {
            User user = session.userTransmission.getUser();
            log.debug("用户{}({})断开连接", user.getUsername(), user.getUserId());
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
        if (cause instanceof ReadTimeoutException) {
            log.debug("已经很久没收到客户端的消息了，要删除Session了");
        } else {
            super.exceptionCaught(ctx, cause);
        }
    }
}
